﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Web;


namespace LynxWiki.Parser
{
    public class WikiToHtmlFormatter
    {
        private StringReader reader;

        public WikiToHtmlFormatter(string wikiText)
        {
            reader = new StringReader(wikiText);
        }

        public void WikiToHtml(TextWriter writer)
        {
            string line = reader.ReadLine();
            while (line != null)
            {
                if (line.StartsWith("!!!!"))
                {
                    WriteHeaderLine(4, line.Substring(4), writer);
                }
                else if (line.StartsWith("!!!"))
                {
                    WriteHeaderLine(3, line.Substring(3), writer);
                }
                else if (line.StartsWith("!!"))
                {
                    WriteHeaderLine(2, line.Substring(2), writer);
                }
                else if (line.StartsWith("!"))
                {
                    WriteHeaderLine(1, line.Substring(1), writer);
                }
                else
                {
                    ProcessLine(line, writer);
                }
                writer.Write("<br />");
                line = reader.ReadLine();
            }
        }

        private void WriteHeaderLine(int headerLevel, string line, TextWriter writer)
        {
            writer.Write(string.Format("<h{0}>", headerLevel));
            ProcessLine(line, writer);
            writer.Write(string.Format("</h{0}>", headerLevel));
        }

        private void ProcessLine(string line, TextWriter writer)
        {
            int state = 0;
            int i = 0;
            int wordStart = 0;
            Action encode = () => HttpUtility.HtmlEncode(line[i].ToString(), writer);
            Func<bool> isws = () => Char.IsWhiteSpace(line[i]);

            for (i = 0; i < line.Length; ++i)
            {
                switch (state)
                {
                    case 0:
                        if (line[i] == '*')
                        {
                            state = 1;
                        }
                        else if (line[i] == '/')
                        {
                            state = 4;
                        }
                        else if (Char.IsUpper(line[i]))
                        {
                            wordStart = i;
                            state = 7;
                        }
                        else
                        {
                            encode();
                        }
                        break;

                    case 1:
                        if (isws())
                        {
                            encode();
                            state = 0;
                        }
                        else
                        {
                            writer.Write("<b>");
                            encode();
                            state = 2;
                        }
                        break;

                    case 2:
                        if (isws())
                        {
                            encode();
                            state = 3;
                        }
                        else if (line[i] == '*')
                        {
                            writer.Write("</b>");
                            state = 0;
                        }
                        else
                        {
                            encode();
                        }
                        break;

                    case 3:
                        if (isws())
                        {
                            encode();
                        }
                        else
                        {
                            encode();
                            state = 2;
                        }
                        break;

                    case 4:
                        if (isws())
                        {
                            HttpUtility.HtmlEncode("/ ", writer);
                            state = 0;
                        }
                        else
                        {
                            writer.Write("<i>");
                            encode();
                            state = 5;
                        }
                        break;

                    case 5:
                        if (isws())
                        {
                            encode();
                            state = 6;
                        }
                        else if (line[i] == '/')
                        {
                            writer.Write("</i>");
                            state = 0;
                        }
                        else
                        {
                            encode();
                        }
                        break;

                    case 6:
                        if (isws())
                        {
                            encode();
                        }
                        else
                        {
                            encode();
                            state = 5;
                        }
                        break;

                    case 7:
                        if (char.IsUpper(line[i]))
                        {
                            state = 8;
                        }
                        else if (!char.IsLetter(line[i]))
                        {
                            HttpUtility.HtmlEncode(line.Substring(wordStart, i - wordStart), writer);
                            encode();
                            state = 0;
                        }
                        break;

                    case 8:
                        if (!char.IsLetter(line[i]))
                        {
                            WriteLink(line.Substring(wordStart, i - wordStart), writer);
                            encode();
                            state = 0;
                        }
                        break;
                }
            }

            // Clean up italics, bold etc. based on the state we were in at the end of the line.
            switch (state)
            {
                case 0:
                    break;
                case 1:
                    HttpUtility.HtmlEncode("*", writer);
                    break;
                case 2:
                case 3:
                    writer.Write("</b>");
                    break;
                case 4:
                    HttpUtility.HtmlEncode("/", writer);
                    break;
                case 5:
                case 6:
                    writer.Write("</i>");
                    break;
                case 7:
                    HttpUtility.HtmlEncode(line.Substring(wordStart), writer);
                    break;
                case 8:
                    WriteLink(line.Substring(wordStart), writer);
                    break;
            }

        }

        private void WriteLink(string wikiWord, TextWriter writer)
        {
            string link = "/" + wikiWord;
            writer.Write("<a href='{0}'>{1}</a>", link, wikiWord);
        }
    }
}
